package org.eclipse.swt.snippets;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Set;
import java.util.TreeSet;

import org.eclipse.jface.layout.TableColumnLayout;
import org.eclipse.jface.preference.PreferenceStore;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.jface.viewers.ColumnPixelData;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetAdapter;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Decorations;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;

public class LogView {

    public static void main(String[] args) throws Exception {
        final Display display = new Display();

        String propDir = System.getenv("APPDATA");
        String props = propDir + File.separator + "foo.props";
        preferenceStore = new PreferenceStore(props);
        try {
            preferenceStore.load();
        } catch (Exception e) {
            preferenceStore.save();
        }
        shell = new Shell(display, SWT.MAX | SWT.MIN | SWT.CLOSE | SWT.RESIZE);
        // SWT.ON_TOP |
        shell.setText("Log viewer");
        final LogView main = new LogView();
        registry = new ImageRegistry();
        main.initializeImageRegistry(registry);
        shell.setMenuBar(main.hookContextMenu(shell));
        main.createPartControl(shell);
        main.setFocus();

        String f = preferenceStore.getString("bookmark.1");
        int i = 1;
        while (!"".equals(f)) {
            i++;
            main.bookmarks.add(new File(f));
            f = preferenceStore.getString("bookmark." + i);
        }
        main.processBookmarks(main.bookmarks.size());

        int w = preferenceStore.getInt("width");
        w = w == 0 ? 1024 : w;
        int h = preferenceStore.getInt("height");
        h = h == 0 ? 768 : h;
        shell.setSize(w, h);
        int x = preferenceStore.getInt("x");
        x = x == 0 ? 10 : x;
        int y = preferenceStore.getInt("y");
        y = y == 0 ? 10 : y;
        shell.setLocation(x, y);
        shell.open();
        shell.addDisposeListener(new DisposeListener() {

            @Override
            public void widgetDisposed(DisposeEvent arg0) {
                main.onClose();

            }
        });
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch())
                display.sleep();
        }
        display.dispose();
    }

    protected void onClose() {
        if (logWatcher != null)
            logWatcher.exit();
        Point location = shell.getLocation();
        Point size = shell.getSize();
        preferenceStore.setValue("width", size.x);
        preferenceStore.setValue("height", size.y);
        preferenceStore.setValue("x", location.x);
        preferenceStore.setValue("y", location.y);
        preferenceStore.setValue("wrap", wrapSearch);
        try {
            preferenceStore.save();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void setFocus() {
        table.setFocus();
    }

    private static Shell           shell;
    private static PreferenceStore preferenceStore;
    private static ImageRegistry   registry;

    private Table                  table;
    private Table                  threads;
    private Table                  classes;
    private ToolItem               scroll;
    private Menu                   bookmarksMenu;
    private ToolItem               bookmarkBtn;
    private Text                   startLine;

    private LinkedList<Filter>     filters    = new LinkedList<Filter>();
    private Set<File>              bookmarks  = new LinkedHashSet<File>();
    private Set<String>            threadSet  = new TreeSet<String>();
    private Set<String>            clazzSet   = new TreeSet<String>();

    private boolean                wrapSearch;
    private String                 searchText = "";

    private LogWatcher             logWatcher;

    public void createPartControl(Composite parent) throws Exception {
        wrapSearch = preferenceStore.getBoolean("wrap");
        parent.setLayout(new GridLayout(2, false));
        final Composite composite = new Composite(parent, SWT.BORDER);
        GridData d = new GridData(GridData.FILL, GridData.FILL, false, true);
        d.widthHint = 200;
        composite.setLayoutData(d);
        createButtons(composite);
        Composite comp = new Composite(parent, SWT.NONE);
        GridData gridData = new GridData(GridData.FILL, GridData.FILL, true, true);
        comp.setLayoutData(gridData);
        TableColumnLayout tablelayout = new TableColumnLayout();
        comp.setLayout(tablelayout);
        table = new Table(comp, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION);
        table.setHeaderVisible(true);

        DropTarget target = new DropTarget(table, DND.DROP_MOVE);
        target.setTransfer(new Transfer[] {FileTransfer.getInstance()});
        target.addDropListener(new DropTargetAdapter() {

            public void drop(DropTargetEvent event) {
                if (event.data == null) {
                    event.detail = DND.DROP_NONE;
                    return;
                }
                String[] files = (String[]) event.data;
                if (files != null && files.length > 0) {
                    openFile(new File(files[0]), true);
                }
            }
        });

        table.addMouseListener(new MouseAdapter() {

            public void mouseDoubleClick(MouseEvent e) {
                TableItem selection = table.getSelection()[0];
                Message msg = (Message) selection.getData();
                showMessage(msg, table.getSelectionIndex());
            }
        });

        String[] names = new String[] {"#", "Date", "Message", "Level", "Thread", "Class"};
        int[] widths = {55, 145, 250, 75, 100, 135};
        for (int i = 0; i < names.length; i++) {
            TableColumn column = new TableColumn(table, SWT.FILL);
            column.setText(names[i]);
            tablelayout.setColumnData(column, new ColumnPixelData(widths[i]));
        }
        tablelayout.setColumnData(table.getColumn(2), new ColumnWeightData(100, 200, true));
    }

    public Menu hookContextMenu(Decorations parent) {
        ToolBar toolBar = new ToolBar(parent, SWT.HORIZONTAL);
        GridData d = new GridData(GridData.FILL, 0, true, false);
        d.horizontalSpan = 2;
        toolBar.setLayoutData(d);

        Menu menuBar = new Menu(parent, SWT.BAR);

        MenuItem fileMenuHeader = new MenuItem(menuBar, SWT.CASCADE);
        fileMenuHeader.setText("&File");

        MenuItem editMenuHeader = new MenuItem(menuBar, SWT.CASCADE);
        editMenuHeader.setText("&Edit");

        MenuItem bookmarksMenuHeader = new MenuItem(menuBar, SWT.CASCADE);
        bookmarksMenuHeader.setText("&Bookmarks");

        bookmarksMenu = new Menu(parent, SWT.DROP_DOWN);
        bookmarksMenuHeader.setMenu(bookmarksMenu);

        Menu fileMenu = new Menu(parent, SWT.DROP_DOWN);
        fileMenuHeader.setMenu(fileMenu);

        ToolMenuItem openFile = new ToolMenuItem(fileMenu, toolBar, SWT.PUSH);
        openFile.setText("Open");
        openFile.setImage(getImage("open"));
        openFile.setAccelerator(SWT.MOD1 | 'N');
        openFile.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                FileDialog dialog = new FileDialog(shell);
                String file = dialog.open();
                if (file == null || "".equals(file)) {
                    return;
                }
                openFile(new File(file), true);
            }
        });

        ToolMenuItem saveFile = new ToolMenuItem(fileMenu, toolBar, SWT.PUSH);
        saveFile.setText("Save");
        saveFile.setImage(getImage("save"));
        saveFile.setAccelerator(SWT.MOD1 | 'S');
        saveFile.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                FileDialog dialog = new FileDialog(shell, SWT.SAVE);
                String file = dialog.open();
                if (file == null || "".equals(file)) {
                    return;
                }
                File f = new File(file);
                if (f.exists()) {
                    return;// FIXME
                }
                try {
                    FileWriter writer = new FileWriter(f);
                    for (TableItem item : table.getItems()) {
                        writer.write(item.getData().toString());
                        writer.write("\n");
                        writer.flush();
                    }
                    writer.close();
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        });

        Menu editMenu = new Menu(parent, SWT.DROP_DOWN);
        editMenuHeader.setMenu(editMenu);

        ToolItem filter = new ToolItem(toolBar, SWT.PUSH);
        filter.setText("Filter");
        filter.setImage(getImage("filter"));
        filter.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                TableItem[] selection = table.getSelection();
                if (selection == null || selection.length < 2) {
                    selection = table.getItems();
                }
                Message[] msgs = new Message[selection.length];
                int i = 0;
                for (TableItem item : selection) {
                    Message msg = (Message) item.getData();
                    msgs[i++] = msg;
                }
                table.removeAll();
                addData(msgs);
            }
        });

        ToolMenuItem reloadFile = new ToolMenuItem(editMenu, toolBar, SWT.PUSH);
        reloadFile.setText("Reload");
        reloadFile.setImage(getImage("update"));
        reloadFile.setAccelerator(SWT.F5);
        reloadFile.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                File file = (File) table.getData();
                if (file != null) {
                    openFile(file, false);
                }
            }
        });

        ToolMenuItem clearFile = new ToolMenuItem(editMenu, toolBar, SWT.PUSH);
        clearFile.setText("Clear");
        clearFile.setImage(getImage("clear"));
        clearFile.setAccelerator(SWT.MOD1 | 'X');
        clearFile.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                table.removeAll();
            }
        });

        new MenuItem(editMenu, SWT.SEPARATOR);

        MenuItem copy = new MenuItem(editMenu, SWT.PUSH);
        copy.setText("Copy\tCtrl+C");
        copy.setImage(getImage("copy"));
        copy.setAccelerator(SWT.MOD1 | 'C');
        copy.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                TableItem[] items = table.getSelection();
                if (items == null || items.length == 0)
                    return;
                StringWriter writer = new StringWriter();
                for (TableItem item : items) {
                    writer.write(item.getData().toString());
                    writer.write("\n");
                    writer.flush();
                }
                Clipboard c = new Clipboard(shell.getDisplay());
                String[] data = new String[] {writer.toString()};
                Transfer[] t = new Transfer[] {TextTransfer.getInstance()};
                c.setContents(data, t);
            }
        });

        new MenuItem(editMenu, SWT.SEPARATOR);
        MenuItem delete = new MenuItem(editMenu, SWT.PUSH);
        delete.setText("Delete\tDel");
        delete.setImage(getImage("delete"));
        delete.setAccelerator(SWT.DEL);
        delete.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                table.remove(table.getSelectionIndices());
            }
        });

        MenuItem select = new MenuItem(editMenu, SWT.PUSH);
        select.setText("Select &All\tCtrl+A");
        select.setAccelerator(SWT.MOD1 | 'A');
        select.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                table.selectAll();
            }
        });

        new MenuItem(editMenu, SWT.SEPARATOR);
        final MenuItem find = new MenuItem(editMenu, SWT.PUSH);
        find.setText("Find...\tCtrl+F");
        find.setAccelerator(SWT.MOD1 | 'F');
        find.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                final Shell dialog = new Shell(shell, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL);
                dialog.setText("Find...");
                GridLayout layout = new GridLayout(2, true);
                dialog.setLayout(layout);

                final Text text = new Text(dialog, SWT.BORDER);
                text.setText(searchText);
                text.selectAll();
                GridData d = new GridData(GridData.FILL, GridData.FILL, true, true);
                d.heightHint = text.getLineHeight();
                d.horizontalSpan = 2;
                text.setLayoutData(d);

                final Button wrapBtn = new Button(dialog, SWT.CHECK);
                wrapBtn.setText("Wrap search");
                wrapBtn.setSelection(wrapSearch);
                d = new GridData(GridData.FILL, GridData.FILL, true, true);
                wrapBtn.setLayoutData(d);

                final Button button = new Button(dialog, 0);
                button.setText("Fi&nd");
                d = new GridData(0, 0, false, true);
                d.horizontalAlignment = SWT.RIGHT;
                button.setLayoutData(d);
                button.addSelectionListener(new SelectionAdapter() {

                    @Override
                    public void widgetSelected(SelectionEvent e) {
                        searchText = text.getText().trim().toLowerCase();
                        wrapSearch = wrapBtn.getSelection();
                        dialog.close();
                        TableItem[] items = table.getItems();
                        if (items.length == 0)
                            return;
                        int selectionIndex = table.getSelectionIndex();
                        for (int i = selectionIndex; i < items.length; i++) {
                            Message msg = (Message) items[i].getData();
                            if (msg.getMessage().toLowerCase().indexOf(searchText) > -1) {
                                table.deselectAll();
                                table.select(i);
                                return;
                            }
                        }
                    }
                });
                dialog.setDefaultButton(button);
                dialog.pack();
                dialog.open();
            }
        });
        MenuItem findNext = new MenuItem(editMenu, SWT.PUSH);
        findNext.setText("Find next\tF3");
        findNext.setAccelerator(SWT.F3);
        findNext.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                if ("".equals(searchText.trim())) {
                    find.notifyListeners(SWT.Selection, null);
                    return;
                }
                TableItem[] items = table.getItems();
                int index = table.getSelectionIndex() + 1;
                int len = items.length;
                if (wrapSearch) {
                    index = index % len;
                }
                for (int i = index; i < len; i++) {
                    Message msg = (Message) items[i].getData();
                    if (msg.getMessage().toLowerCase().indexOf(searchText) > -1) {
                        table.deselectAll();
                        table.select(i);
                        table.setSelection(i);
                        return;
                    }
                }
            }
        });

        MenuItem closeFile = new MenuItem(fileMenu, SWT.PUSH);
        closeFile.setText("&Close\tCtrl+W");
        closeFile.setImage(getImage("close"));
        closeFile.setAccelerator(SWT.MOD1 | 'W');
        closeFile.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                if (logWatcher != null) {
                    logWatcher.exit();
                }
                table.removeAll();
                bookmarkBtn.setSelection(false);
                bookmarkBtn.setEnabled(false);
                shell.setText("Log viewer");
            }
        });

        scroll = new ToolItem(toolBar, SWT.CHECK);
        scroll.setText("Scroll Lock");
        scroll.setImage(getImage("lock"));

        bookmarkBtn = new ToolItem(toolBar, SWT.CHECK);
        bookmarkBtn.setEnabled(false);
        bookmarkBtn.setText("Bookmark");
        bookmarkBtn.setImage(getImage("bookmark"));
        bookmarkBtn.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                if (bookmarkBtn.getSelection()) {
                    addBookmark();
                } else {
                    removeBookmars();
                }
            }
        });

        new MenuItem(fileMenu, SWT.SEPARATOR);
        MenuItem cropFile = new MenuItem(fileMenu, SWT.PUSH);
        cropFile.setText("Crop file");
        cropFile.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                logWatcher.cropFile();
                table.removeAll();
            }
        });

        new MenuItem(fileMenu, SWT.SEPARATOR);
        MenuItem exit = new MenuItem(fileMenu, SWT.PUSH);
        exit.setText("E&xit\tCtrl+Q");
        exit.setAccelerator(SWT.MOD1 | 'Q');
        exit.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                shell.dispose();
            }
        });
        return menuBar;
    }

    public void openFile(File file, boolean skip) {
        shell.setText("Log viewer - " + file);

        if (skip) {
            threadSet = new TreeSet<String>();
            threads.remove(1, threads.getItemCount() - 1);
            clazzSet = new TreeSet<String>();
            classes.remove(1, classes.getItemCount() - 1);

            Iterator<Filter> it = filters.iterator();
            while (it.hasNext()) {
                Filter next = it.next();
                if (next instanceof ThreadFilter || next instanceof ClassFilter) {
                    it.remove();
                }
            }
        }

        table.setData(file);
        bookmarkBtn.setEnabled(true);
        bookmarkBtn.setSelection(bookmarks.contains(file));
        if (logWatcher != null) {
            logWatcher.exit();
        }
        table.removeAll();
        logWatcher = new LogWatcher();
        if (startLine.getEnabled()) {
            String text = startLine.getText();
            if (text != null) {
                try {
                    long line = Long.parseLong(text);
                    logWatcher.setStartLine(line);
                } catch (Exception e) {
                }
            }
        }
        logWatcher.setFile(file);
        logWatcher.setSkip(skip);
        logWatcher.addMessageListener(new MessageListenerImpl());
        new Thread(logWatcher).start();
    }

    private void addBookmark() {
        int size = bookmarks.size();
        bookmarks.add((File) table.getData());
        processBookmarks(size);
    }

    private void processBookmarks(int size) {
        for (int i = 1; i <= size; i++) {
            preferenceStore.setValue("bookmark." + i, "");
        }
        int i = 0;
        MenuItem parentItem = bookmarksMenu.getParentItem();
        Decorations parent = bookmarksMenu.getParent();
        bookmarksMenu.dispose();
        bookmarksMenu = new Menu(parent, SWT.DROP_DOWN);
        parentItem.setMenu(bookmarksMenu);

        for (final File f : bookmarks) {
            i++;
            preferenceStore.setValue("bookmark." + i, f.getAbsolutePath());
            MenuItem item = new MenuItem(bookmarksMenu, SWT.PUSH);
            StringBuffer text = new StringBuffer(f.getName()).append(" - ").append(f.getParent());
            if (i < 10) {
                text.append("\tCtrl+");
                text.append(i);
                item.setAccelerator(SWT.MOD1 | String.valueOf(i).charAt(0));
            }
            item.setText(text.toString());
            item.addSelectionListener(new SelectionAdapter() {

                @Override
                public void widgetSelected(SelectionEvent e) {
                    openFile(f, true);
                }
            });
        }
        try {
            preferenceStore.save();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void removeBookmars() {
        int size = bookmarks.size();
        bookmarks.remove(table.getData());
        processBookmarks(size);
    }

    public void createButtons(Composite parent) throws Exception {
        parent.setLayout(new GridLayout(1, false));

        Group group = new Group(parent, SWT.None);
        group.setText("All");
        GridData d = new GridData(GridData.FILL, 0, true, false);
        group.setLayoutData(d);
        group.setLayout(new GridLayout(1, false));
        final Button btnOnOff = new Button(group, SWT.CHECK);
        btnOnOff.setText("All on/off");
        btnOnOff.setSelection(true);
        btnOnOff.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                if (!btnOnOff.getSelection()) {
                    filters.add(0, new OnOffFilter());
                } else {
                    filters.remove(new OnOffFilter());
                }
            }
        });

        Level[] levels = Level.values();

        group = new Group(parent, SWT.None);
        group.setText("Levels");
        d = new GridData(GridData.FILL, 0, true, false);
        group.setLayoutData(d);
        group.setLayout(new GridLayout(1, false));

        final Table levelsTable = new Table(group, SWT.CHECK);
        levelsTable.setBackground(group.getBackground());
        d = new GridData(GridData.FILL, GridData.FILL, true, true);
        d.heightHint = levelsTable.getItemHeight() * 5 + 3;
        levelsTable.setLayoutData(d);

        final TableItem item0 = new TableItem(levelsTable, 0);
        item0.setChecked(true);
        item0.setText("All on/off");
        for (Level level : levels) {
            TableItem item = new TableItem(levelsTable, 0);
            item.setText(level.toString());
            item.setData(level);
            item.setChecked(true);
        }

        Listener listener = new Listener() {

            @Override
            public void handleEvent(Event e) {
                levelsTable.deselectAll();
                TableItem item = (TableItem) e.item;
                if (e.detail != SWT.CHECK) {
                    item.setChecked(!item.getChecked());
                }
                if (item == item0) {
                    TableItem[] items = levelsTable.getItems();
                    for (int i = 1; i < items.length; i++) {
                        Event event = new Event();
                        event.item = items[i];
                        event.detail = SWT.CHECK;
                        items[i].setChecked(item.getChecked());
                        handleEvent(event);
                    }
                    return;
                }
                Level l = (Level) item.getData();
                if (!item.getChecked()) {
                    Filter filter = new LevelFilter(l);
                    if (!filters.contains(filter))
                        filters.add(filter);
                } else {
                    filters.remove(new LevelFilter(l));
                }
            }
        };
        levelsTable.addListener(SWT.Selection, listener);

        group = new Group(parent, SWT.None);
        group.setText("Threads");
        d = new GridData(GridData.FILL, GridData.FILL, true, false);
        group.setLayoutData(d);
        group.setLayout(new GridLayout(1, false));

        threads = new Table(group, SWT.CHECK);
        threads.setBackground(group.getBackground());
        d = new GridData(GridData.FILL, GridData.FILL, true, true);
        d.heightHint = threads.getItemHeight() * 8 + 3;
        threads.setLayoutData(d);

        final TableItem item1 = new TableItem(threads, 0);
        item1.setChecked(true);
        item1.setText("All on/off");

        Listener l = new Listener() {

            @Override
            public void handleEvent(Event e) {
                threads.deselectAll();
                TableItem item = (TableItem) e.item;
                if (e.detail != SWT.CHECK) {
                    item.setChecked(!item.getChecked());
                }
                if (item == item1) {
                    TableItem[] items = threads.getItems();
                    for (int i = 1; i < items.length; i++) {
                        Event event = new Event();
                        event.item = items[i];
                        event.detail = SWT.CHECK;
                        items[i].setChecked(item.getChecked());
                        handleEvent(event);
                    }
                    return;
                }
                String thread = (String) item.getData();
                if (!item.getChecked()) {
                    Filter filter = new ThreadFilter(thread);
                    if (!filters.contains(filter))
                        filters.add(filter);
                } else {
                    filters.remove(new ThreadFilter(thread));
                }
            }
        };
        threads.addListener(SWT.Selection, l);

        group = new Group(parent, SWT.None);
        group.setText("Classes");
        d = new GridData(GridData.FILL, GridData.FILL, true, true);
        group.setLayoutData(d);
        group.setLayout(new GridLayout(1, false));

        classes = new Table(group, SWT.CHECK);
        classes.setBackground(group.getBackground());
        d = new GridData(GridData.FILL, GridData.FILL, true, true);
        classes.setLayoutData(d);

        final TableItem item2 = new TableItem(classes, 0);
        item2.setChecked(true);
        item2.setText("All on/off");

        l = new Listener() {

            @Override
            public void handleEvent(Event e) {
                classes.deselectAll();
                TableItem item = (TableItem) e.item;
                if (e.detail != SWT.CHECK) {
                    item.setChecked(!item.getChecked());
                }
                if (item == item2) {
                    TableItem[] items = classes.getItems();
                    for (int i = 1; i < items.length; i++) {
                        Event event = new Event();
                        event.item = items[i];
                        event.detail = SWT.CHECK;
                        items[i].setChecked(item.getChecked());
                        handleEvent(event);
                    }
                    return;
                }
                String clazz = (String) item.getData();
                if (!item.getChecked()) {
                    Filter filter = new ClassFilter(clazz);
                    if (!filters.contains(filter))
                        filters.add(filter);
                } else {
                    filters.remove(new ClassFilter(clazz));
                }
            }
        };
        classes.addListener(SWT.Selection, l);

        group = new Group(parent, SWT.None);
        group.setText("Line");
        d = new GridData(GridData.FILL, GridData.FILL, true, false);
        group.setLayoutData(d);
        group.setLayout(new GridLayout(3, false));

        final Button lineEnable = new Button(group, SWT.CHECK);
        d = new GridData(GridData.FILL, GridData.FILL, false, false);
        lineEnable.setLayoutData(d);
        startLine = new Text(group, SWT.BORDER);
        d = new GridData(GridData.FILL, GridData.FILL, true, false);
        startLine.setLayoutData(d);
        // TODO int
        startLine.setEnabled(false);
        final Button b = new Button(group, SWT.None);
        b.setEnabled(false);
        b.setText("<");
        b.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                int idx = table.getSelectionIndex();
                if (idx < 0) {
                    return;
                }
                TableItem item = table.getItem(idx);
                if (item == null) {
                    return;
                }
                Message data = (Message) item.getData();
                if (data == null) {
                    return;
                }
                startLine.setText("" + data.getId());
            }
        });

        lineEnable.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                if (lineEnable.getSelection()) {
                    startLine.setEnabled(true);
                    b.setEnabled(true);
                    startLine.setFocus();
                } else {
                    startLine.setEnabled(false);
                    b.setEnabled(false);
                }
            }
        });
    }

    synchronized public void addData(Message... msgs) {
        messages: for (Message msg : msgs) {
            final String thread = msg.getThread();
            if (threadSet.add(thread)) {
                final TableItem item = new TableItem(threads, 0);
                item.setText(thread);
                item.setChecked(true);
                item.setData(thread);
            }
            final String clazz = msg.getClazz();
            if (clazzSet.add(clazz)) {
                final TableItem item = new TableItem(classes, 0);
                item.setText(clazz);
                item.setChecked(true);
                item.setData(clazz);
            }

            for (Filter f : filters) {
                if (f.filter(msg)) {
                    continue messages;
                }
            }
            TableItem item = new TableItem(table, SWT.NONE);
            setData(item, msg);
            if (!scroll.getSelection()) {
                table.setSelection(item);
            }
        }
    }

    private void setData(TableItem item, Message msg) {
        item.setText(0, String.valueOf(msg.getId()));
        item.setText(1, Message.format.format(msg.getDate()));
        if (msg.getException().length() > 0) {
            item.setImage(2, getImage("stack"));
        }
        item.setText(2, msg.getMessage());
        item.setImage(3, getImage(msg.getLevel().name()));
        item.setText(3, msg.getLevel().toString());
        item.setText(4, msg.getThread());
        item.setText(5, msg.getClazz());
        item.setData(msg);
    }

    public Image getImage(String key) {
        Image image = registry.get(key);
        return image;
    }

    public void initializeImageRegistry(ImageRegistry registry) {
        ImageDescriptor descriptor = ImageDescriptor.createFromFile(LogView.class, "/information.gif");
        registry.put(Level.TRACE.name(), descriptor);
        registry.put(Level.DEBUG.name(), descriptor);
        registry.put(Level.INFO.name(), descriptor);
        descriptor = ImageDescriptor.createFromFile(LogView.class, "/warning.gif");
        registry.put(Level.WARN.name(), descriptor);
        descriptor = ImageDescriptor.createFromFile(LogView.class, "/error.gif");
        registry.put(Level.ERROR.name(), descriptor);
        descriptor = ImageDescriptor.createFromFile(LogView.class, "/stckframe_obj.gif");
        registry.put("stack", descriptor);
        descriptor = ImageDescriptor.createFromFile(LogView.class, "/update.gif");
        registry.put("update", descriptor);
        descriptor = ImageDescriptor.createFromFile(LogView.class, "/bookmark_obj.gif");
        registry.put("bookmark", descriptor);
        descriptor = ImageDescriptor.createFromFile(LogView.class, "/prj_obj.gif");
        registry.put("open", descriptor);
        descriptor = ImageDescriptor.createFromFile(LogView.class, "/save_edit.gif");
        registry.put("save", descriptor);
        descriptor = ImageDescriptor.createFromFile(LogView.class, "/copy_edit.gif");
        registry.put("copy", descriptor);
        descriptor = ImageDescriptor.createFromFile(LogView.class, "/delete_obj.gif");
        registry.put("delete", descriptor);
        descriptor = ImageDescriptor.createFromFile(LogView.class, "/clear.gif");
        registry.put("clear", descriptor);
        descriptor = ImageDescriptor.createFromFile(LogView.class, "/close_view.gif");
        registry.put("close", descriptor);
        descriptor = ImageDescriptor.createFromFile(LogView.class, "/lock_co.gif");
        registry.put("lock", descriptor);
        descriptor = ImageDescriptor.createFromFile(LogView.class, "/up.gif");
        registry.put("up", descriptor);
        descriptor = ImageDescriptor.createFromFile(LogView.class, "/down.gif");
        registry.put("down", descriptor);
        descriptor = ImageDescriptor.createFromFile(LogView.class, "/filter_history.gif");
        registry.put("filter", descriptor);
    }

    public void showMessage(Message msg, int index) {
        final Shell dialog = new Shell(shell, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL | SWT.RESIZE);
        dialog.setData(index);
        dialog.setText("Stack trace");
        GridLayout layout = new GridLayout(2, false);
        dialog.setLayout(layout);

        final Text msgText = new Text(dialog, SWT.BORDER | SWT.WRAP);
        GridData gridData = new GridData(GridData.FILL, GridData.FILL, true, false);
        msgText.setText(msg.getMessage());
        gridData.verticalSpan = 2;
        msgText.setLayoutData(gridData);
        msgText.setEditable(false);

        final Button up = new Button(dialog, 0);
        up.setImage(getImage("up"));
        final Button down = new Button(dialog, 0);
        down.setImage(getImage("down"));
        int count = table.getItemCount();
        if (index >= count - 1) {
            down.setEnabled(false);
        }
        if (index <= 0) {
            up.setEnabled(false);
        }

        final Text excText = new Text(dialog, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
        gridData = new GridData(GridData.FILL, GridData.FILL, true, true);
        gridData.horizontalSpan = 2;
        excText.setLayoutData(gridData);
        excText.setText(msg.getException().toString());
        excText.setEditable(false);
        Point size = dialog.getSize();
        dialog.setSize(Math.min(size.x, 800), Math.min(size.y, 400));
        dialog.open();

        up.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                int idx = (Integer) dialog.getData() - 1;
                down.setEnabled(true);
                Message msg = (Message) table.getItem(idx).getData();
                dialog.setData(idx);
                msgText.setText(msg.getMessage());
                excText.setText(msg.getException().toString());
                if (idx <= 0) {
                    up.setEnabled(false);
                }
            }
        });
        down.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                int idx = (Integer) dialog.getData() + 1;
                int count = table.getItemCount();
                up.setEnabled(true);
                Message msg = (Message) table.getItem(idx).getData();
                dialog.setData(idx);
                msgText.setText(msg.getMessage());
                excText.setText(msg.getException().toString());
                if (idx >= count - 1) {
                    down.setEnabled(false);
                }
            }
        });
    }

    class MessageListenerImpl implements MessageListener {

        @Override
        public void update(final Message msg) {
            if (msg == null)
                return;
            Runnable runnable = new Runnable() {

                @Override
                public void run() {
                    addData(msg);
                }
            };
            shell.getDisplay().syncExec(runnable);
        }
    };
}

// ograniczenie liczby linii
// format daty
// data od
// stay on top
// reload last...
// filtr po message (jak grep)
